/**
 * Based libevent and memcached Http Server 
 * Copyright (C) 2008 ren. All rights reserved.
 *
 * mhttpd is a  open-source web server, mainly used in html/js/cs/images, 
 * such as static resources to provide services on the web. 
 * $Id: mhttpd.d 2008-11-10 by ren $
 */

#include "MHttpd.h"

#include <event.h>
#include <evhttp.h>
#include <libmemcached/memcached.h>
//#include "utilities.h"

void fhttpd_gencb(struct evhttp_request * evreq, void * arg);

/* default configure */
static unsigned short g_is_debug	= IS_DEBUG;
static unsigned short g_is_daemon	= IS_DAEMON;
static unsigned short g_is_browse	= IS_BROWSE;
static unsigned short g_is_log		= IS_LOG;
static unsigned int g_port			= DEFAULT_HTTP_PORT;
static unsigned int g_max_client	= DEFAULT_MAX_CLIENT;
static char g_dir_index[256]		= DEFAULT_DIRECTORY_INDEX;
static char g_doc_root[256]			= DEFAULT_DOCUMENT_ROOT; 
static char g_log_path[256]			= DEFAULT_LOG_PATH;

static char *opt_servers= NULL;        //memcached server list like:  127.0.0.1:11211,127.0.0.1:11212
static int   opt_binary= 0;
static char *opt_hash= NULL;

static memcached_st *memc = NULL;
/* Global variable */
static int g_log_fd					= 0;



/********************************
 *
 *   Http Server Basic Function
 *
 ********************************/

/**
 * Die alert message
 *
 */
void die(char *mess){
    perror(mess); 
    exit(1); 
}

/**
 * substr - Sub string from pos to length
 *
 */
static char *substr( const char *s, int start_pos, int length, char *ret ){
    char buf[length+1];
    int i, j, end_pos;
	int str_len = strlen(s);

    if (str_len <= 0 || length < 0){
		return "";        
	}
	if (length == 0){
		length = str_len - start_pos;
	}
	start_pos = ( start_pos < 0 ? (str_len + start_pos) : ( start_pos==0 ? start_pos : start_pos-- ) );
	end_pos = start_pos + length;

    for(i=start_pos, j=0; i<end_pos && j<=length; i++, j++){
		buf[j] = s[i];        
	}
    buf[length] = '\0';
    strcpy(ret, buf);

    return(ret);
}



/**
 *  filesize - get file size
 */
static long filesize(const char *filename){
    struct stat buf;
    if (!stat(filename, &buf)){
        return buf.st_size;
    }
    return 0;
}

/**
 * file_exists - check file is exist
 */
static int file_exists(const char *filename){
    struct stat buf;
    if (stat(filename, &buf) < 0){
        if (errno == ENOENT){
            return 0;
        }
    }
    return 1;
}

/**
 * file_get_contents - read file contents
 *
 */
static int file_get_contents( const char *filename, size_t filesize, char *ret, off_t length ){
	if ( !file_exists(filename) || access(filename, R_OK)!=0 )	return -1;

	int fd;
	char buf[filesize];

	if ( (fd = open(filename, O_RDONLY)) == -1) return -1;
	length = ( length > 0 ? length : filesize);
	read(fd, buf, length);
	strcpy(ret, buf);
	close(fd);

	return 0;
}

/**
 * is_dir - check file is directory
 * 
 */
static int is_dir(const char *filename){
	struct stat buf;
	if ( stat(filename, &buf) < 0 ){
		return -1;
	}
	if (S_ISDIR(buf.st_mode)){
		return 1;
	}
	return 0;
}

/**
 * is_file - check file is regular file
 * 
 */
static int is_file(const char *filename){
	struct stat buf;
	if ( stat(filename, &buf) < 0 ){
		return -1;
	}
	if (S_ISREG(buf.st_mode)){
		return 1;
	}
	return 0;
}

/**
 * Fetch current date tme
 *
 */
static void getdate(char *s){
	char *wday[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
	time_t timep;
	struct tm *p;

	time(&timep);
	p = localtime(&timep);
	sprintf(s, "%d-%d-%d %d:%d:%d",(1900+p->tm_year), (1+p->tm_mon), p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);
} 

/**
 * Get MIME type header
 *
 */
static void mime_content_type( const char *name, char *ret ){
    char *dot, *buf; 

    dot = strrchr(name, '.'); 

	/* Text */
    if ( strcmp(dot, ".txt") == 0 || strcmp(dot, ".h") == 0 || strcmp(dot, ".c") == 0 || strcmp(dot, ".cpp") == 0 ){
        buf = "text/plain";
    } else if ( strcmp( dot, ".css" ) == 0 ){
        buf = "text/css";
    } else if ( strcmp( dot, ".js" ) == 0 ){
        buf = "text/javascript";
    } else if ( strcmp(dot, ".xml") == 0 || strcmp(dot, ".xsl") == 0 ){
        buf = "text/xml";
    } else if ( strcmp(dot, ".xhtm") == 0 || strcmp(dot, ".xhtml") == 0 || strcmp(dot, ".xht") == 0 ){
        buf = "application/xhtml+xml";
    } else if ( strcmp(dot, ".html") == 0 || strcmp(dot, ".htm") == 0 || strcmp(dot, ".shtml") == 0 || strcmp(dot, ".hts") == 0 ){
        buf = "text/html";

	/* Images */
    } else if ( strcmp( dot, ".gif" ) == 0 ){
        buf = "image/gif";
    } else if ( strcmp( dot, ".png" ) == 0 ){
        buf = "image/png";
    } else if ( strcmp( dot, ".bmp" ) == 0 ){
        buf = "application/x-MS-bmp";
    } else if ( strcmp( dot, ".jpg" ) == 0 || strcmp( dot, ".jpeg" ) == 0 || strcmp( dot, ".jpe" ) == 0 || strcmp( dot, ".jpz" ) == 0 ){
        buf = "image/jpeg";

	/* Audio & Video */
    } else if ( strcmp( dot, ".wav" ) == 0 ){
        buf = "audio/wav";
    } else if ( strcmp( dot, ".wma" ) == 0 ){
        buf = "audio/x-ms-wma";
    } else if ( strcmp( dot, ".wmv" ) == 0 ){
        buf = "audio/x-ms-wmv";
    } else if ( strcmp( dot, ".au" ) == 0 || strcmp( dot, ".snd" ) == 0 ){
        buf = "audio/basic";
    } else if ( strcmp( dot, ".midi" ) == 0 || strcmp( dot, ".mid" ) == 0 ){
        buf = "audio/midi";
    } else if ( strcmp( dot, ".mp3" ) == 0 || strcmp( dot, ".mp2" ) == 0 ){
        buf = "audio/x-mpeg";
	} else if ( strcmp( dot, ".rm" ) == 0  || strcmp( dot, ".rmvb" ) == 0 || strcmp( dot, ".rmm" ) == 0 ){
        buf = "audio/x-pn-realaudio";
    } else if ( strcmp( dot, ".avi" ) == 0 ){
        buf = "video/x-msvideo";
    } else if ( strcmp( dot, ".3gp" ) == 0 ){
        buf = "video/3gpp";
    } else if ( strcmp( dot, ".mov" ) == 0 ){
        buf = "video/quicktime";
    } else if ( strcmp( dot, ".wmx" ) == 0 ){
        buf = "video/x-ms-wmx";
	} else if ( strcmp( dot, ".asf" ) == 0  || strcmp( dot, ".asx" ) == 0 ){
        buf = "video/x-ms-asf";
    } else if ( strcmp( dot, ".mp4" ) == 0 || strcmp( dot, ".mpg4" ) == 0 ){
        buf = "video/mp4";
	} else if ( strcmp( dot, ".mpe" ) == 0  || strcmp( dot, ".mpeg" ) == 0 || strcmp( dot, ".mpg" ) == 0 || strcmp( dot, ".mpga" ) == 0 ){
        buf = "video/mpeg";

	/* Documents */
    } else if ( strcmp( dot, ".pdf" ) == 0 ){
        buf = "application/pdf";
    } else if ( strcmp( dot, ".rtf" ) == 0 ){
        buf = "application/rtf";
	} else if ( strcmp( dot, ".doc" ) == 0  || strcmp( dot, ".dot" ) == 0 ){
        buf = "application/msword";
	} else if ( strcmp( dot, ".xls" ) == 0  || strcmp( dot, ".xla" ) == 0 ){
        buf = "application/msexcel";
	} else if ( strcmp( dot, ".hlp" ) == 0  || strcmp( dot, ".chm" ) == 0 ){
        buf = "application/mshelp";
	} else if ( strcmp( dot, ".swf" ) == 0  || strcmp( dot, ".swfl" ) == 0 || strcmp( dot, ".cab" ) == 0 ){
        buf = "application/x-shockwave-flash";
	} else if ( strcmp( dot, ".ppt" ) == 0  || strcmp( dot, ".ppz" ) == 0 || strcmp( dot, ".pps" ) == 0 || strcmp( dot, ".pot" ) == 0 ){
        buf = "application/mspowerpoint";

	/* Binary & Packages */
    } else if ( strcmp( dot, ".zip" ) == 0 ){
        buf = "application/zip";
    } else if ( strcmp( dot, ".rar" ) == 0 ){
        buf = "application/x-rar-compressed";
    } else if ( strcmp( dot, ".gz" ) == 0 ){
        buf = "application/x-gzip";
    } else if ( strcmp( dot, ".jar" ) == 0 ){
        buf = "application/java-archive";
	} else if ( strcmp( dot, ".tgz" ) == 0  || strcmp( dot, ".tar" ) == 0 ){
        buf = "application/x-tar";
	} else {
		buf = "application/octet-stream";
	}
	strcpy(ret, buf);
}


/********************************
 *
 *   Http Server Core Function
 *
 ********************************/
static void Usage(char *exefile){
	/* Print copyright information */
	fprintf(stderr, "#=======================================\n");
	fprintf(stderr, "# Image Cache Http Server \n");
	fprintf(stderr, "# Version %s\n", VERSION);
	fprintf(stderr, "# \n");
	fprintf(stderr, "# renxijun@gmail.com \n");
	fprintf(stderr, "#=======================================\n\n");
    fprintf(stderr, "Usage: %s [OPTION] ... \n", exefile);

	/* Print options information */
	fprintf(stderr, "\nOptions: \n\
  -D, --is-deubg	Is open debug mode, default No\n\
  -d, --is-daemon	Is daemon running, default No\n\
  -p, --port=PORT	Server listen port, default 80\n\
  -m, --max-client=SIZE	Max connection requests, default 100\n\
  -L, --is-log		Is write access log, default No\n\
  -l, --log-path=PATH	Access log path, default /tmp/imhttpd.log\n\
  -b, --is-browse	Is allow browse file/dir list, default No\n\
  -r, --doc-root=PATH	Web document root directory, default programe current directory ./\n\
  -i, --dir-index=FILE	Directory default index file name, default index.html\n\
  -s, --servers=LIST   all memcached server list\n\
  -h, --help		Print help information\n");

	/* Print example information */
	fprintf(stderr, "\nExample: \n\
  %s -d -p 80 -m 128 -L -l /tmp/access.log -b -r /var/www -i index.html\n\
  %s -d -p80 -m128 -L -l/tmp/access.log -b -r/var/www -iindex.html\n\
  %s --is-daemon --port=80 --max-client=128 --is-log --log-path=/tmp/access.log --is-browse --doc-root=/var/www --dir-index=index.html\n\n", exefile, exefile, exefile);

}

static void PrintConfig(){
	fprintf(stderr, "===================================\n");
	fprintf(stderr, " imhttpd Configure information\n");
	fprintf(stderr, "===================================\n");
	fprintf(stderr, "Is-Debug\t = %s\n", g_is_debug ? "Yes" : "No");
	fprintf(stderr, "Is-Daemon\t = %s\n", g_is_daemon ? "Yes" : "No");
	fprintf(stderr, "Port\t\t = %d\n", g_port);
	fprintf(stderr, "Max-Client\t = %d\n", g_max_client);
	fprintf(stderr, "Is-Log\t\t = %s\n", g_is_log ? "Yes" : "No");
	fprintf(stderr, "Log-Path\t = %s\n", g_log_path);
	fprintf(stderr, "Is-Browse\t = %s\n", g_is_browse ? "Yes" : "No");
	fprintf(stderr, "Doc-Root\t = %s\n", g_doc_root);
	fprintf(stderr, "Dir-Index\t = %s\n", g_dir_index);
	if(opt_servers!=NULL)
		fprintf(stderr, "servers\t = %s\n", opt_servers);
	else 
		fprintf(stderr, "Without Memcached server provided, if you want please input like: --servers=127.0.0.1:11211\n", opt_servers);
	fprintf(stderr, "===================================\n\n");
}

static int WriteLog( const char *message ){
	if ( !g_is_log ){
		fprintf(stderr, "%s", message);
		return 0;
	}
	if ( g_log_fd == 0 ){
		if ( (g_log_fd = open(g_log_path, O_RDWR|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1 ){
			perror("open log file error");
			return -1;
		}
	}
	if (write(g_log_fd, message, strlen(message)) == -1){
		perror("write log error");
		return -1;
	}

	return 0;
}

static int FillHeaders(struct evhttp_request * evreq, struct evbuffer *evbuff, char *filename , 
int status, char *mime_type, off_t length, time_t mod ){
    time_t now;
    char timebuf[100], buf[BUFFER_SIZE], buf_all[REQUEST_MAX_SIZE], log[8];

	memset(buf_all, 0, strlen(buf_all));
	/* Make http head information */
    memset(buf, 0, strlen(buf));
    sprintf(buf, "%s %d %s\r\n", "HTTP/1.0", status, "OK");
    strcat(buf_all, buf);
	
    memset(buf, 0, strlen(buf));
    sprintf(buf, "Server: %s\r\n", SERVER_NAME);
    strcat(buf_all, buf);

    now = time( (time_t*) 0 );
    strftime( timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime( &now ) );
    memset(buf, 0, strlen(buf));
    sprintf(buf, "Date: %s\r\n", timebuf);
    strcat(buf_all, buf);

    if (mime_type != (char*)0){
        memset(buf, 0, strlen(buf));
        sprintf(buf, "Content-Type: %s\r\n", mime_type);
        strcat(buf_all, buf);
    }
    if (length >= 0){
        memset(buf, 0, strlen(buf));
        sprintf(buf, "Content-Length: %lld\r\n", (int64_t)length);        
        strcat(buf_all, buf);
    }
    if (mod != (time_t) -1 ){
        memset(buf, 0, strlen(buf));
        strftime( timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime( &mod ) );
        sprintf(buf, "Last-Modified: %s\r\n", timebuf );
        strcat(buf_all, buf);
    }
    memset(buf, 0, strlen(buf));
    sprintf(buf, "end of head Connection: close\r\n\r\n");
    strcat(buf_all, buf);
    //printf("buf_all=%s\n",buf_all);

	/* Debug message */
	if ( g_is_debug ){
		fprintf(stderr, "[ Response ]\n");
		fprintf(stderr, "%s", buf_all);
	}
	
	//printf("before send header\n");
	evhttp_add_header(evreq->output_headers, buf_all, "");
	return 0;
} 

static int SendError(struct evhttp_request * evreq, struct evbuffer *evbuff, char *filename , int status, char * msg){
    char buf[BUFFER_SIZE], buf_all[REQUEST_MAX_SIZE];

	/* Send http header */
    FillHeaders( evreq,evbuff, filename, status, "text/html", -1, -1 );
    
	/* Send html page */
    memset(buf, 0, strlen(buf));
    sprintf(buf, "<html>\n<head>\n<title>%d %s - </title>\n</head>\n<body>\n<h2>%d %s</h2><br>%s\n", status,  SERVER_NAME, status,  SERVER_NAME,msg);
    evbuffer_add_printf(evbuff,"%s",buf);

    memset(buf, 0, strlen(buf));
    sprintf(buf, "\n<br /><br /><hr />\n<address>%s</address>\n</body>\n</html>\n", SERVER_NAME);
	
	evbuffer_add_printf(evbuff,"%s",buf);	
    evhttp_send_reply(evreq, status, "", evbuff);
    return 0;

}


static int SendFile( struct evhttp_request * evreq, struct evbuffer *evbuff, char *filename ){
    char buf[128], contents[CHUNK_SIZE], mime_type[64];
    int fd;
	size_t file_size;
	
	char * string;
	size_t string_length;
    uint32_t flags;
    memcached_return rc;

    /* Get mime type & send http header information */
    mime_content_type(filename, mime_type);

	/* Read static file write to client socket (File size less CHUNK_SIZE bytes) */
	if(memc!=NULL)  // with memcached server
	{
		string=memcached_get(memc, filename, strlen(filename),&string_length, &flags, &rc);
		
		if (rc == MEMCACHED_SUCCESS) //exist in memcached
	    {
	    	file_size = string_length;
	    	//printf("file_size=%d\n", file_size);
	    	
			if ( file_size < CHUNK_SIZE){				
				FillHeaders( evreq, evbuff, filename, HTTP_OK, mime_type, file_size, 0);
				evbuffer_add_printf(evbuff,"%s",string);
				evhttp_send_reply(evreq, HTTP_OK, "", evbuff);
			}
			else 
			{				
				evhttp_send_reply_start(evreq, HTTP_OK, "the file is too big");
				
				long iter_file_size = CHUNK_SIZE;
				char iter_char = '\0';
				char * start = string;
				int end = 0;
				while(1)
				{
					//printf("ITER_FILE_SIZE=%ld\n", iter_file_size);
					iter_char = string[iter_file_size-1];
					string[iter_file_size-1] = '\0';
					evbuffer_add_printf(evbuff,"%s",start);
					evhttp_send_reply_chunk(evreq, evbuff);
					
					string[iter_file_size-1] = iter_char;
					start = string+iter_file_size-1;
					
					if(end==1 && iter_file_size>=file_size)
						break;					
											
					iter_file_size += (CHUNK_SIZE-1);
					if(end==0 && iter_file_size>=file_size)	
					{
						end = 1;
						iter_file_size=file_size;
					}
				}
				evhttp_send_reply_end(evreq);
			}
	        free(string);
	    }
	    else //(rc != MEMCACHED_SUCCESS)  //not found in memcache
	    {
	    	fprintf(stderr, "memcat: %s: memcache error %s",filename, memcached_strerror(memc, rc));
	    	if (memc->cached_errno)
				fprintf(stderr, " system error %s", strerror(memc->cached_errno));
	    	fprintf(stderr, "\n");
	    	
			/* Open file */
		    if ( (fd = open(filename, O_RDONLY)) == -1 ){
		        memset(buf, '\0', sizeof(buf));
		        sprintf(buf, "Something unexpected went wrong read file %s.", filename);
		        SendError( evreq, evbuff, filename, HTTP_NOTFOUND, buf);
		        
		        return 0;
		    }
		    
			/* Read static file write to client socket (File size less CHUNK_SIZE bytes) */
			file_size = filesize(filename);
			//if(file_size<=MAX_FILE_SIZE-1)
				
			if ( file_size < CHUNK_SIZE){
				int read_result=read(fd, contents, CHUNK_SIZE);
				FillHeaders( evreq, evbuff, filename, HTTP_OK, "", file_size, 0);
				evbuffer_add_printf(evbuff,"%s",contents);
				evhttp_send_reply(evreq, HTTP_OK, "", evbuff);
				
				rc= memcached_set(memc, filename, strlen(filename), contents, read_result,0,0);
			}
			else 
			{
				evhttp_send_reply_start(evreq, HTTP_OK, "OK");
				int first = 1;
				int cached = 1;
				int read_result= read(fd, contents, CHUNK_SIZE);
				//printf("%d\n%s\n\n\n", read_result, contents);
				while(read_result > 0)
				{					
					evbuffer_add_printf(evbuff,"%s",contents);
					evhttp_send_reply_chunk(evreq, evbuff);
															
					if(first && cached)
					{
						rc= memcached_set(memc, filename, strlen(filename), contents, read_result,0,0);
						first = 0;
					}						
					else if(cached)
						rc= memcached_append(memc, filename, strlen(filename), contents, read_result,0,0);		
					
					if (rc != MEMCACHED_SUCCESS)
					{
						fprintf(stderr, "append to memcached error %s, file is %s \n", memcached_strerror(memc, rc), filename);
						rc= memcached_delete(memc, filename, strlen(filename), 0);
						cached = 0;
					}
					
					memset(contents, '\0', sizeof(contents));
					read_result= read(fd, contents, CHUNK_SIZE);				
				}
				evhttp_send_reply_end(evreq);
			}
			
		    /* Close file descriptor */
		    close( fd );
	    }
	} //end of memcached
	else
	{
		/* Open file */
		    if ( (fd = open(filename, O_RDONLY)) == -1 ){
		        memset(buf, '\0', sizeof(buf));
		        sprintf(buf, "Something unexpected went wrong read file %s.", filename);
		        SendError( evreq, evbuff, filename, HTTP_NOTFOUND, buf);
		        
		        return 0;
		    }
		    
			/* Read static file write to client socket (File size less CHUNK_SIZE bytes) */
			file_size = filesize(filename);
			if ( file_size < CHUNK_SIZE){
				read(fd, contents, CHUNK_SIZE);
				FillHeaders( evreq, evbuff, filename, HTTP_OK, "", file_size, 0);
				evbuffer_add_printf(evbuff,"%s",contents);
				evhttp_send_reply(evreq, HTTP_OK, "", evbuff);
			}
			else 
			{
				evhttp_send_reply_start(evreq, HTTP_OK, "OK");
				
				while(read(fd, contents, CHUNK_SIZE) > 0)
				{
					evbuffer_add_printf(evbuff,"%s",contents);
					evhttp_send_reply_chunk(evreq, evbuff);
					memset(contents, '\0', sizeof(contents));
				}
				evhttp_send_reply_end(evreq);
			}
			
		    /* Close file descriptor */
		    close( fd );
	}
	  
    /* Debug message */
    if (g_is_debug){
        printf("request filename: %s\n", filename);
        //printf("request filename: %s\n", pathinfo);
    }

	return 0;
}


static int SendDirectory( struct evhttp_request * evreq, struct evbuffer *evbuff, char *path ){
	size_t file_size;
	char msg[1024], buf[10240], tmp[1024], tmp_path[1024];
	DIR *dp;
	struct dirent *node;
	char mime_type[64];

	/* Open directory */
	if ( (dp = opendir(path)) == NULL ){
		memset(msg, 0, sizeof(msg));
		sprintf(msg, "Something unexpected went wrong browse directory %s.", path);		
		SendError( evreq, evbuff, path, HTTP_SERVUNAVAIL,msg);		
		return 0;
	}
	
	/* Check pathinfo is valid */
	memset(tmp_path, 0, sizeof(tmp_path));
	substr(path, -1, 1, tmp_path);
	if ( strcmp(tmp_path, "/") == 0){
		memset(tmp_path, 0, sizeof(tmp_path));
		strcpy(tmp_path, path);
	} else {
		memset(tmp_path, 0, sizeof(tmp_path));
		strcpy(tmp_path, path);	
		strcat(tmp_path, "/");
	}
	
	FillHeaders( evreq, evbuff, path, HTTP_OK,  mime_type, 0, 0);
	
	memset(buf, 0, sizeof(buf));
	/* Fetch directory list */
	sprintf( tmp, "<html>\n<head>\n<title>Browse directory %s - %s</title>\n</head>\n<body><h3>Directory %s</h3>\n<hr />\n<ul>\n", path, SERVER_NAME, path );
	strcat(buf, tmp);
	//evbuffer_add_printf(evbuff,"%s",tmp);

	char physical_path[2018] =  "";
	strcat(physical_path,path);
	
	int count = 0;
	while ( (node = readdir(dp)) != NULL){
		if ( strcmp(node->d_name, ".") != 0){
			
			memset(physical_path, 0, sizeof(physical_path));
			strcat(physical_path,path);
			strcat(physical_path,node->d_name);
			
			memset(tmp, 0, sizeof(tmp));
			if(is_dir(physical_path) == 1)
				sprintf(tmp, "\t<li><a href=\"%s/\">%s</a></li>\n", node->d_name, node->d_name);
			else
				sprintf(tmp, "\t<li><a href=\"%s\">%s</a></li>\n", node->d_name, node->d_name);
			
			strcat(buf, tmp);
			if(count>=100)
			{
				evbuffer_add_printf(evbuff,"%s",buf);
				memset(buf, 0, sizeof(buf));
				count=0;
			}
			++count;
		}
	}
    memset(tmp, 0, sizeof(tmp));
    sprintf(tmp, "\n</ul>\n<br /><hr />\n<address>%s</address>\n</body>\n</html>\n", SERVER_NAME);
    strcat(buf, tmp);
    evbuffer_add_printf(evbuff,"%s",buf);

	//printf("before send list dir\n");
	evhttp_send_reply(evreq, HTTP_OK, "", evbuff);
	
	return 0;
}

static int GetResponse( struct evhttp_request * evreq, struct evbuffer *evbuff ){
	char currtime[32], log[1024];
	
	getdate(currtime);

	memset(log, 0, sizeof(log));
	sprintf(log, "[%s] %s %d %s\n", currtime, evreq->remote_host, evreq->remote_port, evreq->uri);
	WriteLog(log);
	//printf("\n\nstart response===========%s\n", evreq->uri);
	/* Debug message */
	if ( g_is_debug ){
		fprintf(stderr, "[ Request ]\n");
		fprintf(stderr, "[%s] %s %d %s\n", currtime, evreq->remote_host, evreq->remote_port, evreq->uri);
	}
	
	char buf[1024];
	/* Process client request */
	/* File is exist or has access permission */
	char physical_path[2018] =  ".";
	//printf(">>>>>>>>>%s\n", evreq->uri);
	strcat(physical_path,evreq->uri);
	
	if(memc==NULL)
	{
		if ( !file_exists( physical_path ) ){
		memset(buf, 0, sizeof(buf));
		sprintf(buf, "File %s not found.", physical_path);		
		SendError( evreq, evbuff, physical_path, HTTP_NOTFOUND, buf);
		return 0;
		}
		if ( access(physical_path, R_OK) != 0 ){
			memset(buf, 0, sizeof(buf));
			sprintf(buf, "File %s is protected, you can not access this file.", physical_path);		
			SendError( evreq, evbuff, physical_path, HTTP_NOTFOUND, buf);
			return 0;
		}
	}
	
	/* Check target is regular file or directory */
	if ( physical_path[strlen(physical_path)-1] != '/' )
	{
		SendFile(evreq, evbuff, physical_path);
	} 
	else if ( is_dir(physical_path) == 1 )
	{ 
		/* Is a directory choose browse dir list */
		if ( g_is_browse )
		{
			//printf("===========%s\n", physical_path);
			SendDirectory( evreq, evbuff, physical_path);
		} 
		else 
		{
			memset(buf, 0, sizeof(buf));
			sprintf(buf, "Path %s is protected.", physical_path);
			SendError( evreq, evbuff,  physical_path, HTTP_NOTFOUND,buf);
		}
		return 0;
	} 
	else
	{
		memset(buf, 0, sizeof(buf));
		sprintf(buf, "Object %s is protected.", physical_path);
		SendError( evreq, evbuff,  physical_path, HTTP_NOTFOUND,buf);	
	}
		
	return 0;
}

void fhttpd_gencb(struct evhttp_request * evreq, void * arg)
{
	//printf("\n\nenter callback\n");
	struct evbuffer *evbuff = NULL;

	//fprintf(stderr, "[%s] %s\n", evreq->remote_host, evreq->uri);

	/* create a evbuffer variable for response */
	evbuff = evbuffer_new();
	if(NULL == evbuff)
	{
		perror("evbuffer_new");
		return;
	}
	//printf("before enter response\n");
	GetResponse(evreq, evbuff);
	
	evbuffer_free(evbuff);
	
}


/** 
 * Parse cmd options
 *
 */
static int ParseOptions( int argc, char *argv[] ){
	int opt;
	struct option longopts[] = {
		{ "is-debug",	0, NULL, 'D' },
		{ "is-daemon",	0, NULL, 'd' },
		{ "port",		1, NULL, 'p' },
		{ "max-client",	1, NULL, 'm' },
		{ "is-log",		0, NULL, 'L' },
 		{ "log-path",	1, NULL, 'l' },
		{ "is-browse",	0, NULL, 'b' },
		{ "doc-root",	1, NULL, 'r' },
		{ "dir-index",	1, NULL, 'i' },
		{ "servers",    1, NULL, 's' },
		{ "hash",       1, NULL, 'a' },
		{ "binary",     1, NULL, 'o' },
		{ "help",		0, NULL, 'h' },
		{ 0,			0, 0,	 0   }
	};

	/* Parse every options */
	while ( (opt = getopt_long(argc, argv, ":Ddp:m:Ll:br:i:s:h", longopts, NULL)) != -1){
		switch (opt){
			case 'h': 
				Usage(argv[0]); 
				return(-1);
				break;
			case 'D': g_is_debug = 1; break;
			case 'd': g_is_daemon = 1; break;
			case 'p':
				g_port = atoi(optarg);
				if ( g_port < 1 || g_port > 65535 ){
					fprintf(stderr, "Options -p,--port error: input port number %s invalid, must between 1 - 65535.\n\n", optarg);
					return(-1);
				}
				break;
			case 'm':
				g_max_client = atoi(optarg);
				if ( !isdigit(g_max_client) ){
					fprintf(stderr, "Options -m,--max-client error: input clients %s invalid, must number, proposal between 32 - 2048.\n\n", optarg);
					return(-1);
				}
				break;
			case 'L': g_is_log = 1; break;
			case 'l':
				strcpy(g_log_path, optarg);
				if ( !file_exists(g_log_path) || !is_dir(g_log_path) ){
					fprintf(stderr, "Options -l,--log-path error: input path %s not exist or not a directory.\n\n", optarg);
					return(-1);					
				}
				break;
			case 'b': g_is_browse = 1; break;
			case 'r':
				strcpy(g_doc_root, optarg);
				if ( !file_exists(g_doc_root) || !is_dir(g_doc_root) ){
					fprintf(stderr, "Options -l,--log-path error: input path %s not exist or not a directory.\n\n", optarg);
					return(-1);					
				}
				break;
			case 'i':
				strcpy(g_dir_index, optarg);
				break;
				
			case 's': /* --servers or -s */
      			opt_servers= strdup(optarg);
      			break;
      		case 'o':	/*case OPT_BINARY*/
      			opt_binary = 1;
      			break;
      		case 'a':  /* case OPT_HASH*/
      			opt_hash= strdup(optarg);
      			break;
		}
	}

	return(0);
}

void process_hash_option(memcached_st *memc, char *opt_hash)
{
  uint64_t set;
  memcached_return rc;

  if (opt_hash == NULL)
    return;

  set= MEMCACHED_HASH_DEFAULT; /* Just here to solve warning */
  if (!strcasecmp(opt_hash, "CRC"))
    set= MEMCACHED_HASH_CRC;
  else if (!strcasecmp(opt_hash, "FNV1_64"))
    set= MEMCACHED_HASH_FNV1_64;
  else if (!strcasecmp(opt_hash, "FNV1A_64"))
    set= MEMCACHED_HASH_FNV1A_64;
  else if (!strcasecmp(opt_hash, "FNV1_32"))
    set= MEMCACHED_HASH_FNV1_32;
  else if (!strcasecmp(opt_hash, "FNV1A_32"))
    set= MEMCACHED_HASH_FNV1A_32;
  else
  {
    fprintf(stderr, "hash: type not recognized %s\n", opt_hash);
    exit(1);
  }

  rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_HASH, set);
  if (rc != MEMCACHED_SUCCESS)
  {
    fprintf(stderr, "hash: memcache error %s\n", memcached_strerror(memc, rc));
    exit(1);
  }
}


void initMemcached(char * server_list)
{
    memcached_return rc;
    memcached_server_st *servers;
    
	memc= memcached_create(NULL);
  	process_hash_option(memc, opt_hash);
  	
  	servers= memcached_servers_parse(opt_servers);
  	
  	memcached_server_push(memc, servers);
  	memcached_server_list_free(servers);
  	memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, opt_binary);
  	  	
  
	return ;
}
/********************************
 *
 *   Http Server running
 *
 ********************************/
int main(int argc, char** argv)
{
	struct event_base *evbase = NULL;
	struct evhttp *evhttp = NULL;
	unsigned short port = 8080;
	const char *host = "0.0.0.0";
	
	if ( argc > 1 ){
		if ( ParseOptions( argc, argv ) != 0 ){
			exit(-1);
		}
	}
	
	port = g_port;
	
	if (!opt_servers)
	{
		fprintf(stderr, "No Memcached server provided, so this httpd server just read from hard disk\n");
		printf("No Memcached server provided, so this httpd server just read from hard disk :) \n");
	}
	else
		initMemcached(opt_servers);
	
	// Set work directory 
	chdir(g_doc_root);
	
	// Set is daemon mode run 
	if ( g_is_daemon ){
		pid_t pid;
		if ( (pid = fork()) < 0 ){
			die("daemon fork error");
		} else if ( pid != 0){
			exit(1);
		}
	}

	// Debug mode out configure information 
	if ( g_is_debug ){
		PrintConfig();
	}

	/* 1). event initialization */
	evbase = event_init();
	if(NULL == evbase)
	{
		perror("event_base_new");
		return 1;
	}

	/* 2). event http initialization */
	evhttp = evhttp_new(evbase);
	if(NULL == evhttp)
	{
		perror("evhttp_new");
		return 2;
	}

	/* 3). set general callback of http request */
	evhttp_set_gencb(evhttp, &fhttpd_gencb, NULL);

	/* 4). bind socket */
	if(0 != evhttp_bind_socket(evhttp, host, port))
	{
		perror("evhttp_bind_socket");	
		return 3;
	}

	fprintf(stderr, "http server is running (%s:%d)\n", host, port);

	/* 5). start http server */
	if(0 == event_base_dispatch(evbase))
	{
		perror("event_base_dispatch");
		return 4;
	}

	/* 6). free resource before exit */
	evhttp_free(evhttp);
	event_base_free(evbase);
	
	if(memc)
		memcached_free(memc);
  	if (opt_servers)
    	free(opt_servers);
  	if (opt_hash)
    	free(opt_hash);	
    	
	return 0;
}   
    
    
    